package net.sourceforge.jweb.jstl.helper;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import javax.servlet.jsp.tagext.SimpleTagSupport;

import org.apache.commons.io.FileUtils;
import org.reflections.Reflections;
import org.reflections.scanners.Scanner;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.scanners.TypeAnnotationsScanner;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import org.reflections.util.FilterBuilder;
import org.springframework.util.StringUtils;

import net.sourceforge.jweb.annotation.Attribute;
import net.sourceforge.jweb.annotation.Tag;
import net.sourceforge.jweb.annotation.Taglib;
import net.sourceforge.jweb.util.FreeMarkerUtil;

public class TldGenerator {
	public static void main(String args[]) throws IntrospectionException, IOException {
		String packagePrefix = args[0];
		ConfigurationBuilder builder = new ConfigurationBuilder();
		builder.setScanners(new Scanner[] { new TypeAnnotationsScanner(), new SubTypesScanner(false) });
		builder.filterInputsBy(new FilterBuilder().include(FilterBuilder.prefix(packagePrefix)));
		builder.setUrls(ClasspathHelper.forPackage(packagePrefix, new ClassLoader[0]));

		Reflections reflections = new Reflections(builder);
		Set<Class<? extends Object>> allClasses = reflections.getTypesAnnotatedWith(Tag.class);

		StringBuilder b = new StringBuilder(2048);

		boolean taglibGet = false;
		String tablibString = "";
		for (Class<?> c : allClasses) {
			System.out.println("processing tag class: " + c.getName());
			Taglib taglib = c.getDeclaredAnnotation(Taglib.class);
			if (taglib != null) {
				if (taglibGet) {
					System.out.println("duplicated taglib annotation");
				} else {
					System.out.println("loaded taglib on class " + c.getName());
					tablibString = FreeMarkerUtil.processTemplate("tld-begin.fmt", "version", taglib.version(),
							"shortName", taglib.shortName(), "uri", taglib.uri(), "desc", taglib.description());
				}
				taglibGet = true;
			}

			Map<String, Object> tagMap = new HashMap<>();
			tagMap.put("desc", c.getName());
			tagMap.put("name", c.getSimpleName().replace("Tag", "").toLowerCase());
			tagMap.put("className", c.getName());
			tagMap.put("content", c.getSuperclass().equals(SimpleTagSupport.class) ? "empty" : "JSP");

			Tag tag = c.getDeclaredAnnotation(Tag.class);
			if (tag != null) {
				if (StringUtils.hasText(tag.description()))
					tagMap.put("desc", tag.description());
				if (StringUtils.hasText(tag.name()))
					tagMap.put("name", tag.name());
			}
			BeanInfo binfo = Introspector.getBeanInfo(c);
			b.append(FreeMarkerUtil.processTemplate("tag-begin.fmt", tagMap));
			b.append("\n");
			System.out.println("Porcessing tag " + c.getName());
			PropertyDescriptor[] properties = binfo.getPropertyDescriptors();
			for (PropertyDescriptor descriptor : properties) {
				Method readMethod = descriptor.getReadMethod();
				Method writeMethod = descriptor.getWriteMethod();
				if (readMethod == null || writeMethod == null) {
					continue;
				}
				String classOfMethod = readMethod.getDeclaringClass().getName();
				if (!classOfMethod.startsWith(packagePrefix)) {
					continue;
				}
				System.out.println("Porcessing attribute " + descriptor.getName());

				Map<String, Object> tagAttrMap = new HashMap<>();
				tagAttrMap.put("name", descriptor.getName());
				tagAttrMap.put("required", "false");
				tagAttrMap.put("type", descriptor.getPropertyType().getName());
				tagAttrMap.put("rtexprvalue", "true");
				tagAttrMap.put("description", descriptor.getName());

				Attribute attr = readMethod.getDeclaredAnnotation(Attribute.class);
				if (attr != null) {
					tagAttrMap.put("required", attr.required() ? "true" : "false");
					if (StringUtils.hasText(attr.type())) {
						tagAttrMap.put("type", attr.type());
					}
					tagAttrMap.put("rtexprvalue", attr.rtexprvalue() ? "true" : "false");
					if (StringUtils.hasText(attr.description())) {
						tagAttrMap.put("description", attr.description());
					}
				}

				b.append(FreeMarkerUtil.processTemplate("attr-begin.fmt", tagAttrMap));
				b.append("\n");
			}
			b.append("\t</tag>\n");
		}
		b.append("</taglib>");
		System.out.println("Done");
		FileUtils.write(new File("easyui-jstl.tld"), tablibString + "\n");
		FileUtils.write(new File("easyui-jstl.tld"), b.toString(), true);
	}
}
